﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SqlServer.Management.Common;
using Microsoft.SqlServer.Management.Smo;
using System.Collections.Specialized;

namespace SQLDataTransferer
{
    public static class SmoFactory
    {
        #region Events

        public delegate void TransferProgressEventHandler(string message);
        public static event TransferProgressEventHandler TransferProgress;

        #endregion

        #region Methods

        public static ServerConnection CreateConnection(string serverInstance)
        {
            return new ServerConnection(serverInstance);
        }

        public static ServerConnection CreateConnection(string serverInstance, string userName, string password)
        {
            return new ServerConnection
            {
                ServerInstance = serverInstance,
                LoginSecure = false,
                Login = userName,
                Password = password
            };
        }

        public static ScriptingOptions CreateOptions(SqlServerVersion version)
        {
            return new ScriptingOptions
            {
                TargetServerVersion = version,
                AnsiPadding = true,
                ContinueScriptingOnError = false,
                ScriptDrops = false,
                WithDependencies = false,
                IncludeIfNotExists = false,
                IncludeHeaders = true,
                Triggers = true,
                AllowSystemObjects = true,
                DriAll = true,
                ScriptData = true //version == SqlServerVersion.Version100
            };
        }

        public static Server GetServer(ServerConnection connection)
        {
            if (connection == null)
                throw new ArgumentNullException("connection");

            return new Server(connection);
        }

        public static Database GetDatabase(Server server, string name)
        {
            if (server == null)
                throw new ArgumentNullException("server");

            if (string.IsNullOrEmpty(name))
                throw new ArgumentNullException("name");

            return server.Databases[name];
        }

        public static StringCollection GenerateScripts(Database database, ScriptingOptions options)
        {
            if (database == null)
                throw new ArgumentNullException("database");

            if (options == null)
                return database.Script();

            return database.Script(options);
        }

        public static void SetCompatibilityLevel(Database database, SqlServerVersion version)
        {
            if (database == null)
                throw new ArgumentNullException("database");

            // sql server 2000; value = 80
            //database.CompatibilityLevel = CompatibilityLevel.Version80; 
            // sql server 2005; value = 90
            //database.CompatibilityLevel = CompatibilityLevel.Version90;
            // sql server 2008; value = 100
            //database.CompatibilityLevel = CompatibilityLevel.Version100;

            database.CompatibilityLevel = (CompatibilityLevel)Enum.Parse(typeof(CompatibilityLevel), Enum.GetName(typeof(SqlServerVersion), version));
        }
        
        public static void AttachDatabase(Server server, string name, StringCollection files, string owner)
        {
            if (server == null)
                throw new ArgumentNullException("server");

            //TODO: Check arguments 

            if (string.IsNullOrEmpty(owner))
            {
                server.AttachDatabase(name, files);
                return;
            }

            server.AttachDatabase(name, files, owner);
        }

        public static void DetachDatabase(Server server, string name)
        {
            if (server == null)
                throw new ArgumentNullException("server");

            //TODO: Check arguments 

            server.DetachDatabase(name, false);
        }

        /// <summary>
        /// Transfers a database from one server to another (can be the same)
        /// </summary>
        /// <param name="from">Host server</param>
        /// <param name="dbToCopy">Host servers database, database to copy</param>
        /// <param name="to">Destination server</param>
        /// <param name="db">database to create or copy to</param>
        public static void TransferDatabase(Server from, Database dbToCopy, Server to, Database db)
        {
            // fetch all properties when the objects get Instantiated
            from.SetDefaultInitFields(true);

            // Check if the database allready exists on the destination server
            // If not exists create first
            if (!to.Databases.Contains(db.Name))
                db.Create();

            // Host configuration
            Transfer transfer = new Transfer(dbToCopy);

            transfer.DataTransferEvent += new DataTransferEventHandler(transfer_DataTransferEvent);
            transfer.ScriptingProgress += new ProgressReportEventHandler(transfer_ScriptingProgress);

            transfer.CopyAllObjects = true;
            transfer.CopySchema = true;
            transfer.CopyData = true;

            //transfer.CopyAllTables = true;
            //transfer.CopyAllDatabaseTriggers = true;
            //transfer.CopyAllDefaults = true;
            //transfer.CopyAllFullTextCatalogs = true;
            //transfer.CopyAllFullTextStopLists = true;
            //transfer.CopyAllPartitionFunctions = true;
            //transfer.CopyAllPartitionSchemes = true;
            //transfer.CopyAllPlanGuides = true;
            //transfer.CopyAllRoles = true;
            //transfer.CopyAllRules = true;
            //transfer.CopyAllSchemas = true;
            //transfer.CopyAllSqlAssemblies = true;
            //transfer.CopyAllStoredProcedures = true;
            //transfer.CopyAllSynonyms = true;
            //transfer.CopyAllXmlSchemaCollections = true;
            //transfer.CopyAllViews = true;
            //transfer.CopyAllUsers = true;
            //transfer.CopyAllUserDefinedTypes = true;
            //transfer.CopyAllUserDefinedFunctions = true;
            //transfer.CopyAllUserDefinedDataTypes = true;
            //transfer.CopyAllUserDefinedAggregates = true;
            //transfer.DropDestinationObjectsFirst = true;
            transfer.UseDestinationTransaction = true;

            // Destination configuration
            transfer.DestinationServer = to.Name;
            transfer.DestinationDatabase = db.Name;
            transfer.DestinationLoginSecure = to.ConnectionContext.LoginSecure;
            transfer.DestinationPassword = to.ConnectionContext.Password;
            transfer.DestinationLogin = to.ConnectionContext.Login;

            // Options
            transfer.Options.IncludeIfNotExists = true;
            transfer.Options.WithDependencies = true;
            transfer.Options.ContinueScriptingOnError = true;
            transfer.Options.PrimaryObject = true;
            transfer.Options.AllowSystemObjects = true;
            transfer.Options.Indexes = true;
            transfer.Options.Triggers = true;
            transfer.Options.DriAll = true;

            //transfer.Options.DriUniqueKeys = true;
            //transfer.Options.DriPrimaryKey = true;
            //transfer.Options.DriIndexes = true;
            //transfer.Options.DriForeignKeys = true;


            transfer.TransferData();
        }

        static void transfer_ScriptingProgress(object sender, ProgressReportEventArgs e)
        {
            if (TransferProgress != null)
                TransferProgress(e.Current.Value);
        }

        static void transfer_DataTransferEvent(object sender, DataTransferEventArgs e)
        {
            //if (e.DataTransferEventType != DataTransferEventType.Progress)
            //    return;

            if (TransferProgress != null)
                TransferProgress(e.Message);
        }

        #endregion
    }
}
